/*
 * @Description: 
 * @version: 
 * @Author: Eve
 * @Date: 2019-10-07 15:38:51
 * @LastEditors: Eve
 * @LastEditTime: 2019-10-07 17:50:12
 */
const PENDING = 'pending'
const RESOLVE = 'fulfilled'
const REJECT = 'rejected'
class Promiser {
	_status = PENDING
	_result
	constructor(fn) {
		// 根据之前描述构造器作用主要是运行传入的fn
		// 将fn放在try catch中运行，防止fn执行出错
		try {
			//new Promise((resolve, reject) => {})
			fn(this.resolve.bind(this), this.reject.bind(this))
		} catch (e) {
			this.reject(e)
		}
	}
	static resolve() {}
	static reject() {}
	// resolve调用之后改变后promise的状态，并且异步执行callback
	resolve(result) {
		// promise的状态不是pending,说明该promise已经调用过reject/resolve
		if (this._status !== PENDING) throw new Error('重复决议promise')
		// 保存决议结果
		this._result = result
		this._status = RESOLVE
		// 异步执行callback
		this._runCallbackAsync('success')
	}

	// reject也是同理
	reject(err) {
		if (this._status !== PENDING) throw new Error('重复决议promise')
		this._result = err
		this._status = REJECT
		// 这里我们执行错误回调
		this._runCallbackAsync('error')
	}
	/**
     * then称得上是promise的核心方法，到底then做了什么，我们考虑一下
     * then会接收两个函数，在promise的状态发生改变后会调用对应的函数
     * 所以在这里then的作用应当是个事件注册器。
     * 需要注意的是then是能多次调用的
     * const promise = new Promise(fn)
     * promise.then(fn1)
     * promise.then(fn2)
     * 另外then是支持链式调用的，如promise.then(fn3).then(fn4)
     * 所以调用then还应当返回一个promise对象
     **/
	then(fn, fn2) {
		/* 
    resolve->fn
    reject->fn2
    支持链式调用
    */
		if (this._status === PENDING) {
			fn && this._successCallback.push(fn)
			fn2 && this._errorCallback.push(fn2)
		}
		if (this._status === RESOLVE) {
			fn && fn(this._result)
		}
		if (this._status === REJECT) {
			fn2 && fn2(this._result)
		}
		if (this._status === 'success') this._runCallbackAsync('success')
		if (this._status === 'error') this._runCallbackAsync('error')
		return this // 实际上此处未能实现链式传参 只实现了链式调用 应该return 一个Promise对象
	}

	// 描述完then后，我们发现我们需要两个回调队列来保存使用then注册的回调
	_successCallback = []
	_errorCallback = []

	// 我们再定义一个内部方法来异步执行这些回调函数
	// 使用入参type区分执行successCallback还是errorCallback
	// 实现
	_runCallbackAsync(type) {
		let eventQueue
		eventQueue = type === 'error' ? this._errorCallback : this._successCallback
		// 执行回调, 使用settimeout模拟异步执行
		setTimeout(() => {
			eventQueue.forEach(callback => callback(this._result))
			// 清空事件队列，两个事件队列都需要删除，因为promise决议后状态不可变更，决议执行完应当清空所有队列，以解除引用关系
			this._errorCallback.length = 0
			this._successCallback.length = 0
		}, 0)
	}
}

let demo = new Promiser((res, rej) => {
	setTimeout(() => {
		res('我输出了')
	}, 2000)
})
